/** @file
  Main file for endfor and for shell level 1 functions.

  (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
  Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "UefiShellLevel1CommandsLib.h"
#include <Library/PrintLib.h>

/**
  Determine if a valid string is a valid number for the 'for' command.

  @param[in] Number The pointer to the string representation of the number to test.

  @retval TRUE    The number is valid.
  @retval FALSE   The number is not valid.
**/
BOOLEAN
ShellIsValidForNumber (
  IN CONST CHAR16 *Number
  )
{
  if (Number == NULL || *Number == CHAR_NULL) {
    return (FALSE);
  }

  if (*Number == L'-') {
    Number++;
  }

  if (StrLen(Number) == 0) {
    return (FALSE);
  }

  if (StrLen(Number) >= 7) {
    if ((StrStr(Number, L" ") == NULL) || (((StrStr(Number, L" ") != NULL) && (StrStr(Number, L" ") - Number) >= 7))) {
      return (FALSE);
    }
  }

  if (!ShellIsDecimalDigitCharacter(*Number)) {
    return (FALSE);
  }

  return (TRUE);
}

/**
  Function for 'endfor' command.

  @param[in] ImageHandle  Handle to the Image (NULL if Internal).
  @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
**/
SHELL_STATUS
EFIAPI
ShellCommandRunEndFor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS          Status;
  BOOLEAN             Found;
  SCRIPT_FILE         *CurrentScriptFile;

  Status = CommandInit();
  ASSERT_EFI_ERROR(Status);

  if (!gEfiShellProtocol->BatchIsActive()) {
    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_NO_SCRIPT), gShellLevel1HiiHandle, L"endfor");
    return (SHELL_UNSUPPORTED);
  }

  if (gEfiShellParametersProtocol->Argc > 1) {
    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel1HiiHandle, L"endfor");
    return (SHELL_INVALID_PARAMETER);
  }

  Found = MoveToTag(GetPreviousNode, L"for", L"endfor", NULL, ShellCommandGetCurrentScriptFile(), FALSE, FALSE, FALSE);

  if (!Found) {
    CurrentScriptFile = ShellCommandGetCurrentScriptFile();
    ShellPrintHiiEx(
      -1,
      -1,
      NULL,
      STRING_TOKEN (STR_SYNTAX_NO_MATCHING),
      gShellLevel1HiiHandle,
      L"For",
      L"EndFor",
      CurrentScriptFile!=NULL
        && CurrentScriptFile->CurrentCommand!=NULL
          ? CurrentScriptFile->CurrentCommand->Line:0);
    return (SHELL_NOT_FOUND);
  }
  return (SHELL_SUCCESS);
}

typedef struct {
  UINT32          Signature;
  INTN            Current;
  INTN            End;
  INTN            Step;
  CHAR16          *ReplacementName;
  CHAR16          *CurrentValue;
  BOOLEAN         RemoveSubstAlias;
  CHAR16          Set[1];
  } SHELL_FOR_INFO;
#define SIZE_OF_SHELL_FOR_INFO OFFSET_OF (SHELL_FOR_INFO, Set)
#define SHELL_FOR_INFO_SIGNATURE SIGNATURE_32 ('S', 'F', 'I', 's')

/**
  Update the value of a given alias on the list.  If the alias is not there then add it.

  @param[in] Alias               The alias to test for.
  @param[in] CommandString       The updated command string.
  @param[in, out] List           The list to search.

  @retval EFI_SUCCESS           The operation was completed successfully.
  @retval EFI_OUT_OF_RESOURCES  There was not enough free memory.
**/
EFI_STATUS
InternalUpdateAliasOnList(
  IN CONST CHAR16       *Alias,
  IN CONST CHAR16       *CommandString,
  IN OUT LIST_ENTRY     *List
  )
{
  ALIAS_LIST *Node;
  BOOLEAN    Found;

  //
  // assert for NULL parameter
  //
  ASSERT(Alias != NULL);

  //
  // check for the Alias
  //
  for ( Node = (ALIAS_LIST *)GetFirstNode(List), Found = FALSE
      ; !IsNull(List, &Node->Link)
      ; Node = (ALIAS_LIST *)GetNextNode(List, &Node->Link)
     ){
    ASSERT(Node->CommandString != NULL);
    ASSERT(Node->Alias != NULL);
    if (StrCmp(Node->Alias, Alias)==0) {
      FreePool(Node->CommandString);
      Node->CommandString = NULL;
      Node->CommandString = StrnCatGrow(&Node->CommandString, NULL, CommandString, 0);
      Found = TRUE;
      break;
    }
  }
  if (!Found) {
    Node = AllocateZeroPool(sizeof(ALIAS_LIST));
    if (Node == NULL) {
      return (EFI_OUT_OF_RESOURCES);
    }
    ASSERT(Node->Alias == NULL);
    Node->Alias         = StrnCatGrow(&Node->Alias, NULL, Alias, 0);
    ASSERT(Node->CommandString == NULL);
    Node->CommandString = StrnCatGrow(&Node->CommandString, NULL, CommandString, 0);
    InsertTailList(List, &Node->Link);
  }
  return (EFI_SUCCESS);
}

/**
  Find out if an alias is on the given list.

  @param[in] Alias              The alias to test for.
  @param[in] List               The list to search.

  @retval TRUE                  The alias is on the list.
  @retval FALSE                 The alias is not on the list.
**/
BOOLEAN
InternalIsAliasOnList(
  IN CONST CHAR16       *Alias,
  IN CONST LIST_ENTRY   *List
  )
{
  ALIAS_LIST *Node;

  //
  // assert for NULL parameter
  //
  ASSERT(Alias != NULL);

  //
  // check for the Alias
  //
  for ( Node = (ALIAS_LIST *)GetFirstNode(List)
      ; !IsNull(List, &Node->Link)
      ; Node = (ALIAS_LIST *)GetNextNode(List, &Node->Link)
     ){
    ASSERT(Node->CommandString != NULL);
    ASSERT(Node->Alias != NULL);
    if (StrCmp(Node->Alias, Alias)==0) {
      return (TRUE);
    }
  }
  return (FALSE);
}

/**
  Remove an alias from the given list.

  @param[in] Alias               The alias to remove.
  @param[in, out] List           The list to search.
**/
BOOLEAN
InternalRemoveAliasFromList(
  IN CONST CHAR16       *Alias,
  IN OUT LIST_ENTRY     *List
  )
{
  ALIAS_LIST *Node;

  //
  // assert for NULL parameter
  //
  ASSERT(Alias != NULL);

  //
  // check for the Alias
  //
  for ( Node = (ALIAS_LIST *)GetFirstNode(List)
      ; !IsNull(List, &Node->Link)
      ; Node = (ALIAS_LIST *)GetNextNode(List, &Node->Link)
     ){
    ASSERT(Node->CommandString != NULL);
    ASSERT(Node->Alias != NULL);
    if (StrCmp(Node->Alias, Alias)==0) {
      RemoveEntryList(&Node->Link);
      FreePool(Node->Alias);
      FreePool(Node->CommandString);
      FreePool(Node);
      return (TRUE);
    }
  }
  return (FALSE);
}

/**
  Function to determine whether a string is decimal or hex representation of a number
  and return the number converted from the string.

  @param[in] String   String representation of a number

  @return             the number
  @retval (UINTN)(-1) An error ocurred.
**/
UINTN
ReturnUintn(
  IN CONST CHAR16 *String
  )
{
  UINT64        RetVal;

  if (!EFI_ERROR(ShellConvertStringToUint64(String, &RetVal, FALSE, TRUE))) {
    return ((UINTN)RetVal);
  }
  return ((UINTN)(-1));
}

/**
  Function for 'for' command.

  @param[in] ImageHandle  Handle to the Image (NULL if Internal).
  @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
**/
SHELL_STATUS
EFIAPI
ShellCommandRunFor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS          Status;
  SHELL_STATUS        ShellStatus;
  SCRIPT_FILE         *CurrentScriptFile;
  CHAR16              *ArgSet;
  CHAR16              *ArgSetWalker;
  CHAR16              *Parameter;
  UINTN               ArgSize;
  UINTN               LoopVar;
  SHELL_FOR_INFO      *Info;
  CHAR16              *TempString;
  CHAR16              *TempSpot;
  BOOLEAN             FirstPass;
  EFI_SHELL_FILE_INFO *Node;
  EFI_SHELL_FILE_INFO *FileList;
  UINTN               NewSize;

  ArgSet              = NULL;
  ArgSize             = 0;
  ShellStatus         = SHELL_SUCCESS;
  ArgSetWalker        = NULL;
  TempString          = NULL;
  Parameter           = NULL;
  FirstPass           = FALSE;

  //
  // initialize the shell lib (we must be in non-auto-init...)
  //
  Status = ShellInitialize();
  ASSERT_EFI_ERROR(Status);

  Status = CommandInit();
  ASSERT_EFI_ERROR(Status);

  if (!gEfiShellProtocol->BatchIsActive()) {
    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_NO_SCRIPT), gShellLevel1HiiHandle, L"for");
    return (SHELL_UNSUPPORTED);
  }

  if (gEfiShellParametersProtocol->Argc < 4) {
    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel1HiiHandle, L"for");
    return (SHELL_INVALID_PARAMETER);
  }

  CurrentScriptFile = ShellCommandGetCurrentScriptFile();
  ASSERT(CurrentScriptFile != NULL);

  if ((CurrentScriptFile->CurrentCommand != NULL) && (CurrentScriptFile->CurrentCommand->Data == NULL)) {
    FirstPass = TRUE;

    //
    // Make sure that an End exists.
    //
    if (!MoveToTag(GetNextNode, L"endfor", L"for", NULL, CurrentScriptFile, TRUE, TRUE, FALSE)) {
      ShellPrintHiiEx(
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_SYNTAX_NO_MATCHING),
        gShellLevel1HiiHandle,
        L"EndFor",
        L"For",
        CurrentScriptFile->CurrentCommand->Line);
      return (SHELL_DEVICE_ERROR);
    }

    //
    // Process the line.
    //
    if (gEfiShellParametersProtocol->Argv[1][0] != L'%' || gEfiShellParametersProtocol->Argv[1][2] != CHAR_NULL
      ||!((gEfiShellParametersProtocol->Argv[1][1] >= L'a' && gEfiShellParametersProtocol->Argv[1][1] <= L'z')
       ||(gEfiShellParametersProtocol->Argv[1][1] >= L'A' && gEfiShellParametersProtocol->Argv[1][1] <= L'Z'))
     ) {
      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_INV_VAR), gShellLevel1HiiHandle, gEfiShellParametersProtocol->Argv[1]);
      return (SHELL_INVALID_PARAMETER);
    }

    if (gUnicodeCollation->StriColl(
        gUnicodeCollation,
        L"in",
        gEfiShellParametersProtocol->Argv[2]) == 0) {
      for (LoopVar = 0x3 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
        ASSERT((ArgSet == NULL && ArgSize == 0) || (ArgSet != NULL));
        if (StrStr(gEfiShellParametersProtocol->Argv[LoopVar], L"*") != NULL
          ||StrStr(gEfiShellParametersProtocol->Argv[LoopVar], L"?") != NULL
          ||StrStr(gEfiShellParametersProtocol->Argv[LoopVar], L"[") != NULL
          ||StrStr(gEfiShellParametersProtocol->Argv[LoopVar], L"]") != NULL) {
          FileList = NULL;
          Status = ShellOpenFileMetaArg ((CHAR16*)gEfiShellParametersProtocol->Argv[LoopVar], EFI_FILE_MODE_READ, &FileList);
          if (EFI_ERROR(Status) || FileList == NULL || IsListEmpty(&FileList->Link)) {
            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" \"", 0);
            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, gEfiShellParametersProtocol->Argv[LoopVar], 0);
            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
          } else {
            for (Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&FileList->Link)
              ;  !IsNull(&FileList->Link, &Node->Link)
              ;  Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&FileList->Link, &Node->Link)
             ){
              ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" \"", 0);
              ArgSet = StrnCatGrow(&ArgSet, &ArgSize, Node->FullName, 0);
              ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
            }
            ShellCloseFileMetaArg(&FileList);
          }
        } else {
          Parameter = gEfiShellParametersProtocol->Argv[LoopVar];
          if (Parameter[0] == L'\"' && Parameter[StrLen(Parameter)-1] == L'\"') {
            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" ", 0);
            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, Parameter, 0);
          } else {
            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" \"", 0);
            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, Parameter, 0);
            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
          }
        }
      }
      if (ArgSet == NULL) {
        ShellStatus = SHELL_OUT_OF_RESOURCES;
      } else {
        //
        // set up for an 'in' for loop
        //
        NewSize = StrSize(ArgSet);
        NewSize += sizeof(SHELL_FOR_INFO)+StrSize(gEfiShellParametersProtocol->Argv[1]);
        Info = AllocateZeroPool(NewSize);
        if (Info == NULL) {
          FreePool (ArgSet);
          return SHELL_OUT_OF_RESOURCES;
        }
        Info->Signature = SHELL_FOR_INFO_SIGNATURE;
        CopyMem(Info->Set, ArgSet, StrSize(ArgSet));
        NewSize = StrSize(gEfiShellParametersProtocol->Argv[1]);
        CopyMem(Info->Set+(StrSize(ArgSet)/sizeof(Info->Set[0])), gEfiShellParametersProtocol->Argv[1], NewSize);
        Info->ReplacementName = Info->Set+StrSize(ArgSet)/sizeof(Info->Set[0]);
        Info->CurrentValue  = (CHAR16*)Info->Set;
        Info->Step          = 0;
        Info->Current       = 0;
        Info->End           = 0;

        if (InternalIsAliasOnList(Info->ReplacementName, &CurrentScriptFile->SubstList)) {
          Info->RemoveSubstAlias  = FALSE;
        } else {
          Info->RemoveSubstAlias  = TRUE;
        }
        CurrentScriptFile->CurrentCommand->Data = Info;
      }
    } else if (gUnicodeCollation->StriColl(
        gUnicodeCollation,
        L"run",
        gEfiShellParametersProtocol->Argv[2]) == 0) {
      for (LoopVar = 0x3 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
        ASSERT((ArgSet == NULL && ArgSize == 0) || (ArgSet != NULL));
        if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L")") != NULL &&
            (LoopVar + 1) < gEfiShellParametersProtocol->Argc
           ) {
          return (SHELL_INVALID_PARAMETER);
        }
        if (ArgSet == NULL) {
//        ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
        } else {
          ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" ", 0);
        }
        ArgSet = StrnCatGrow(&ArgSet, &ArgSize, gEfiShellParametersProtocol->Argv[LoopVar], 0);
//        ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" ", 0);
      }
      if (ArgSet == NULL) {
        ShellStatus = SHELL_OUT_OF_RESOURCES;
      } else {
        //
        // set up for a 'run' for loop
        //
        Info = AllocateZeroPool(sizeof(SHELL_FOR_INFO)+StrSize(gEfiShellParametersProtocol->Argv[1]));
        if (Info == NULL) {
          FreePool (ArgSet);
          return SHELL_OUT_OF_RESOURCES;
        }
        Info->Signature = SHELL_FOR_INFO_SIGNATURE;
        CopyMem(Info->Set, gEfiShellParametersProtocol->Argv[1], StrSize(gEfiShellParametersProtocol->Argv[1]));
        Info->ReplacementName = Info->Set;
        Info->CurrentValue    = NULL;
        ArgSetWalker            = ArgSet;
        if (ArgSetWalker[0] != L'(') {
          ShellPrintHiiEx(
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
            gShellLevel1HiiHandle,
            ArgSet,
            CurrentScriptFile->CurrentCommand->Line);
          ShellStatus = SHELL_INVALID_PARAMETER;
        } else {
          TempSpot = StrStr(ArgSetWalker, L")");
          if (TempSpot != NULL) {
            TempString = TempSpot+1;
            if (*(TempString) != CHAR_NULL) {
              while(TempString != NULL && *TempString == L' ') {
                TempString++;
              }
              if (StrLen(TempString) > 0) {
                TempSpot = NULL;
              }
            }
          }
          if (TempSpot == NULL) {
            ShellPrintHiiEx(
              -1,
              -1,
              NULL,
              STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
              gShellLevel1HiiHandle,
              CurrentScriptFile->CurrentCommand->Line);
            ShellStatus = SHELL_INVALID_PARAMETER;
          } else {
            *TempSpot = CHAR_NULL;
            ArgSetWalker++;
            while (ArgSetWalker != NULL && ArgSetWalker[0] == L' ') {
              ArgSetWalker++;
            }
            if (!ShellIsValidForNumber(ArgSetWalker)) {
              ShellPrintHiiEx(
                -1,
                -1,
                NULL,
                STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
                gShellLevel1HiiHandle,
                ArgSet,
                CurrentScriptFile->CurrentCommand->Line);
              ShellStatus = SHELL_INVALID_PARAMETER;
            } else {
              if (ArgSetWalker[0] == L'-') {
                Info->Current = 0 - (INTN)ReturnUintn(ArgSetWalker+1);
              } else {
                Info->Current = (INTN)ReturnUintn(ArgSetWalker);
              }
              ArgSetWalker  = StrStr(ArgSetWalker, L" ");
              while (ArgSetWalker != NULL && ArgSetWalker[0] == L' ') {
                ArgSetWalker++;
              }
              if (ArgSetWalker == NULL || *ArgSetWalker == CHAR_NULL || !ShellIsValidForNumber(ArgSetWalker)){
                ShellPrintHiiEx(
                  -1,
                  -1,
                  NULL,
                  STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
                  gShellLevel1HiiHandle,
                  ArgSet,
                  CurrentScriptFile->CurrentCommand->Line);
                ShellStatus = SHELL_INVALID_PARAMETER;
              } else {
                if (ArgSetWalker[0] == L'-') {
                  Info->End = 0 - (INTN)ReturnUintn(ArgSetWalker+1);
                } else {
                  Info->End = (INTN)ReturnUintn(ArgSetWalker);
                }
                if (Info->Current < Info->End) {
                  Info->Step            = 1;
                } else {
                  Info->Step            = -1;
                }

                ArgSetWalker  = StrStr(ArgSetWalker, L" ");
                while (ArgSetWalker != NULL && ArgSetWalker[0] == L' ') {
                  ArgSetWalker++;
                }
                if (ArgSetWalker != NULL && *ArgSetWalker != CHAR_NULL) {
                  if (ArgSetWalker == NULL || *ArgSetWalker == CHAR_NULL || !ShellIsValidForNumber(ArgSetWalker)){
                    ShellPrintHiiEx(
                      -1,
                      -1,
                      NULL,
                      STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
                      gShellLevel1HiiHandle,
                      ArgSet,
                      CurrentScriptFile->CurrentCommand->Line);
                    ShellStatus = SHELL_INVALID_PARAMETER;
                  } else {
                    if (*ArgSetWalker == L')') {
                      ASSERT(Info->Step == 1 || Info->Step == -1);
                    } else {
                      if (ArgSetWalker[0] == L'-') {
                        Info->Step = 0 - (INTN)ReturnUintn(ArgSetWalker+1);
                      } else {
                        Info->Step = (INTN)ReturnUintn(ArgSetWalker);
                      }

                      if (StrStr(ArgSetWalker, L" ") != NULL) {
                        ShellPrintHiiEx(
                          -1,
                          -1,
                          NULL,
                          STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
                          gShellLevel1HiiHandle,
                          ArgSet,
                          CurrentScriptFile->CurrentCommand->Line);
                        ShellStatus = SHELL_INVALID_PARAMETER;
                      }
                    }
                  }

                }
              }
            }
          }
        }
        if (ShellStatus == SHELL_SUCCESS) {
          if (InternalIsAliasOnList(Info->ReplacementName, &CurrentScriptFile->SubstList)) {
            Info->RemoveSubstAlias  = FALSE;
          } else {
            Info->RemoveSubstAlias  = TRUE;
          }
        }
        if (CurrentScriptFile->CurrentCommand != NULL) {
          CurrentScriptFile->CurrentCommand->Data = Info;
        }
      }
    } else {
      ShellPrintHiiEx(
        -1,
        -1,
        NULL,
        STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
        gShellLevel1HiiHandle,
        ArgSet,
        CurrentScriptFile!=NULL
          && CurrentScriptFile->CurrentCommand!=NULL
          ? CurrentScriptFile->CurrentCommand->Line:0);
      ShellStatus = SHELL_INVALID_PARAMETER;
    }
  } else {
    //
    // These need to be NULL since they are used to determine if this is the first pass later on...
    //
    ASSERT(ArgSetWalker == NULL);
    ASSERT(ArgSet       == NULL);
  }

  if (CurrentScriptFile != NULL && CurrentScriptFile->CurrentCommand != NULL) {
    Info = (SHELL_FOR_INFO*)CurrentScriptFile->CurrentCommand->Data;
    if (CurrentScriptFile->CurrentCommand->Reset) {
      if (Info != NULL) {
        Info->CurrentValue = (CHAR16*)Info->Set;
      }
      FirstPass = TRUE;
      CurrentScriptFile->CurrentCommand->Reset = FALSE;
    }
  } else {
    ShellStatus = SHELL_UNSUPPORTED;
    Info = NULL;
  }
  if (ShellStatus == SHELL_SUCCESS) {
    ASSERT(Info != NULL);
    if (Info->Step != 0) {
      //
      // only advance if not the first pass
      //
      if (!FirstPass) {
        //
        // sequence version of for loop...
        //
        Info->Current += Info->Step;
      }

      TempString = AllocateZeroPool(50*sizeof(CHAR16));
      UnicodeSPrint(TempString, 50*sizeof(CHAR16), L"%d", Info->Current);
      InternalUpdateAliasOnList(Info->ReplacementName, TempString, &CurrentScriptFile->SubstList);
      FreePool(TempString);

      if ((Info->Step > 0 && Info->Current > Info->End) || (Info->Step < 0 && Info->Current < Info->End)) {
        CurrentScriptFile->CurrentCommand->Data = NULL;
        //
        // find the matching endfor (we're done with the loop)
        //
        if (!MoveToTag(GetNextNode, L"endfor", L"for", NULL, CurrentScriptFile, TRUE, FALSE, FALSE)) {
          ShellPrintHiiEx(
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_SYNTAX_NO_MATCHING),
            gShellLevel1HiiHandle,
            L"EndFor",
            L"For",
            CurrentScriptFile!=NULL
              && CurrentScriptFile->CurrentCommand!=NULL
              ? CurrentScriptFile->CurrentCommand->Line:0);
          ShellStatus = SHELL_DEVICE_ERROR;
        }
        if (Info->RemoveSubstAlias) {
          //
          // remove item from list
          //
          InternalRemoveAliasFromList(Info->ReplacementName, &CurrentScriptFile->SubstList);
        }
        FreePool(Info);
      }
    } else {
      //
      // Must be in 'in' version of for loop...
      //
      ASSERT(Info->Set != NULL);
      if (Info->CurrentValue != NULL && *Info->CurrentValue != CHAR_NULL) {
        if (Info->CurrentValue[0] == L' ') {
          Info->CurrentValue++;
        }
        if (Info->CurrentValue[0] == L'\"') {
          Info->CurrentValue++;
        }
        //
        // do the next one of the set
        //
        ASSERT(TempString == NULL);
        TempString = StrnCatGrow(&TempString, NULL, Info->CurrentValue, 0);
        if (TempString == NULL) {
          ShellStatus = SHELL_OUT_OF_RESOURCES;
        } else {
          TempSpot   = StrStr(TempString, L"\" \"");
          if (TempSpot != NULL) {
            *TempSpot = CHAR_NULL;
          }
          while (TempString[StrLen(TempString)-1] == L'\"') {
            TempString[StrLen(TempString)-1] = CHAR_NULL;
          }
          InternalUpdateAliasOnList(Info->ReplacementName, TempString, &CurrentScriptFile->SubstList);
          Info->CurrentValue += StrLen(TempString);

          if (Info->CurrentValue[0] == L'\"') {
            Info->CurrentValue++;
          }
          FreePool(TempString);
        }
      } else {
        CurrentScriptFile->CurrentCommand->Data = NULL;
        //
        // find the matching endfor (we're done with the loop)
        //
        if (!MoveToTag(GetNextNode, L"endfor", L"for", NULL, CurrentScriptFile, TRUE, FALSE, FALSE)) {
          ShellPrintHiiEx(
            -1,
            -1,
            NULL,
            STRING_TOKEN (STR_SYNTAX_NO_MATCHING),
            gShellLevel1HiiHandle,
            L"EndFor",
            L"For",
            CurrentScriptFile!=NULL
              && CurrentScriptFile->CurrentCommand!=NULL
              ? CurrentScriptFile->CurrentCommand->Line:0);
          ShellStatus = SHELL_DEVICE_ERROR;
        }
        if (Info->RemoveSubstAlias) {
          //
          // remove item from list
          //
          InternalRemoveAliasFromList(Info->ReplacementName, &CurrentScriptFile->SubstList);
        }
        FreePool(Info);
      }
    }
  }
  if (ArgSet != NULL) {
    FreePool(ArgSet);
  }
  return (ShellStatus);
}

